﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Aleator.Resources;

namespace Aleator.Core
{
    /// <summary>
    /// Partial class to hold progression list
    /// Note: when using the XSD tool to generate the partial classes in the /Core directory, remember to change the namespace to Aleator.Core
    /// remove the [System.Diagnostics.DebuggerStepThroughAttribute()] completely, and rename the classes according to convention.
    /// </summary>
    public partial class Composition
    {
        #region Members

        private static PhraseFactory m_phraseFactory = new ConcretePhraseFactory();
        private static ProgressionFactory m_progressionFactory = new ConcreteProgressionFactory();

        private List<ConcreteProgression> m_progressions;
        private ConcreteProgression m_currentProgression;

        private List<Melody> m_melodies;
        private Melody m_currentMelody;

        private CircleOfFifths.Key m_keySignature;

        private static XmlDocument m_doc;
        private static XmlNode m_root;
        private static XmlNodeList m_progressionList;

        private XmlNodeList m_degreeList;

        private bool m_acoustic;
        #endregion

        #region Properties

        /// <summary>
        /// Quarter note always accounts for a "beat" for now
        /// </summary>
        public const int Denominator = 4;

        /// <summary>
        /// List of progressions for this composition
        /// </summary>
        public List<ConcreteProgression> Progressions
        {
            get
            {
                if (m_progressions == null)
                    m_progressions = new List<ConcreteProgression>();

                return m_progressions;
            }
        }

       

        /// <summary>
        /// Progression currently being handled by the session
        /// </summary>
        public ConcreteProgression CurrentProgression
        {
            get
            {
                return m_currentProgression;
            }
            set
            {
                if (Progressions.Contains(value))
                    m_currentProgression = value;
            }
        }

        /// <summary>
        /// List of progressions for this composition
        /// </summary>
        public List<Melody> Melodies
        {
            get
            {
                if (m_melodies == null)
                    m_melodies = new List<Melody>();

                return m_melodies;
            }
        }

        /// <summary>
        /// Progression currently being handled by the session
        /// </summary>
        public Melody CurrentMelody
        {
            get
            {
                return m_currentMelody;
            }
            set
            {
                if (Melodies.Contains(value))
                    m_currentMelody = value;
            }
        }

        /// <summary>
        /// This property is used as a placeholder for naming progression keys as progressions are being loaded
        /// </summary>
        public CircleOfFifths.Key KeySignature
        {
            get
            {
                return m_keySignature;
            }
            set
            {
                m_keySignature = value;
            }
        }

        public bool Acoustic
        {
            get
            {
                return m_acoustic;
            }
            set
            {
                m_acoustic = value;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Main constructor
        /// </summary>
        public Composition()
        {

            m_doc = new XmlDocument();
            m_doc.Load(Aleator.Resources.AppResources.ProgressionDocumentPath);

            //m_doc.LoadXml(Properties.Resources.Progression);

            m_root = m_doc.SelectSingleNode("/Progressions");

            m_progressionList = m_root.SelectNodes("Progression");           

            //Need to make this configurable. We are doing 1/3 omnisphere drums for Facets
            m_acoustic = Session.RndGenerator.Next(6) > 1;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Load a progression from XML
        /// </summary>
        /// <param name="comp">Progression XML node from the composition document</param>
        public void LoadProgression(XmlNode comp)
        {
            byte progressionID = Convert.ToByte(comp.InnerText);

            XmlNode pn = m_progressionList.Item(progressionID - 1);

            ConcreteProgression p = (ConcreteProgression)m_progressionFactory.GetProgression(this, comp, pn);

            m_degreeList = pn.SelectNodes("Degree");

            Chord.ScaleDegree degree;

            int totalbeats = 0;
            int count = 1;

            //TODO: This currently only adds chords for the first cycle
            for (int i = 0; i < p.Multiplier; i++)
            {
                foreach (XmlNode dl in m_degreeList)
                {
                    degree = (Chord.ScaleDegree)System.Enum.Parse(typeof(Chord.ScaleDegree), dl.InnerText);
                    Chord c = CircleOfFifths.Chords.Where(ch => ch.Degree == degree).First();

                    if (c != null)
                    {
                        p.Chords.Add(totalbeats, c);

                        System.Diagnostics.Debug.WriteLine(string.Format("Adding progression degree {0} at location {1}", c.Degree, totalbeats));

                        if (i == 0)
                        {
                            p.ScaleDegrees += c.Degree;

                            if (count < m_degreeList.Count)
                                p.ScaleDegrees += " - ";
                        }
                    }

                    totalbeats += Convert.ToInt16(dl.Attributes.GetNamedItem("Beats").InnerText);
                    count++;
                }

                if (i == 0)
                    p.BeatsPerCycle = totalbeats;
            }

            p.TotalBeats = totalbeats;
            p.LoadMelodies();

            Aleator.Core.ConcreteProgression oldprogression = null;

            if (Progressions.Where(pr => pr.ProgressionID == p.ProgressionID).Any())
                oldprogression = Progressions.Where(pr => pr.ProgressionID == p.ProgressionID).First();

            //TODO: More random use of instrumentation
            if (oldprogression != null)
            {
                //This is not the first instance of the progression in the composition - direct copy
                p.DrumPhrase = (DrumPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Drum, p, oldprogression.DrumPhrase);
                p.RhythmPhrase = (RhythmPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Rhythm, p, oldprogression.RhythmPhrase);
                p.BassPhrase = (BassPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Bass, p, oldprogression.BassPhrase);
                p.MelodyPhrase = (MelodyPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Melody, p, oldprogression.MelodyPhrase);
            }
            else if (Progressions.Where(pr => pr.Numerator == p.Numerator).Any())
            {
                //This is not the first progression in the composition with the current numerator - use commonality for drums
                p.DrumPhrase = (DrumPhrase)m_phraseFactory.ExplodePhrase(PhraseFactory.PhraseType.Drum, p);
                
                p.RhythmPhrase = (RhythmPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Rhythm, p);
                p.BassPhrase = (BassPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Bass, p);
                p.MelodyPhrase = (MelodyPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Melody, p);
            }
            else
            {
                //This is the first progression in the composition with the current numerator - create from scratch
                p.DrumPhrase = (DrumPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Drum, p);

                p.RhythmPhrase = (RhythmPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Rhythm, p);
                p.BassPhrase = (BassPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Bass, p);
                p.MelodyPhrase = (MelodyPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Melody, p);
            }

            Progressions.Add(p);
        }

        #endregion
    }
}
